package com.thiswhyme.fsplus.feign.httpclient;

import feign.Client;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PreDestroy;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @description: copy from org.springframework.cloud.openfeign.ribbon.HttpClientFeignLoadBalancedConfiguration
 * 修改：使用自定义ThwmApacheHttpClient
 * @author: thiswhyme
 * @date: 2021/03/06
 */
@Configuration
@ConditionalOnClass(ThwmApacheHttpClient.class)
@ConditionalOnProperty(name = "feign.sentinel.fsplus.ip-source-enabled", havingValue = "true")
public class ThwmHttpClientFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory, HttpClient httpClient) {
        ThwmApacheHttpClient delegate = new ThwmApacheHttpClient(httpClient);
        return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
    }

    @Configuration
    @ConditionalOnMissingBean(CloseableHttpClient.class)
    protected static class HttpClientFeignConfiguration {

        private final Timer connectionManagerTimer = new Timer(
                "FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

        private CloseableHttpClient httpClient;

        @Autowired(required = false)
        private RegistryBuilder registryBuilder;

        @Bean
        @ConditionalOnMissingBean(HttpClientConnectionManager.class)
        public HttpClientConnectionManager connectionManager(
                ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
                FeignHttpClientProperties httpClientProperties) {
            final HttpClientConnectionManager connectionManager = connectionManagerFactory
                    .newConnectionManager(httpClientProperties.isDisableSslValidation(),
                            httpClientProperties.getMaxConnections(),
                            httpClientProperties.getMaxConnectionsPerRoute(),
                            httpClientProperties.getTimeToLive(),
                            httpClientProperties.getTimeToLiveUnit(),
                            this.registryBuilder);
            this.connectionManagerTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    connectionManager.closeExpiredConnections();
                }
            }, 30000, httpClientProperties.getConnectionTimerRepeat());
            return connectionManager;
        }

        @Bean
        @ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true")
        public CloseableHttpClient customHttpClient(
                HttpClientConnectionManager httpClientConnectionManager,
                FeignHttpClientProperties httpClientProperties) {
            HttpClientBuilder builder = HttpClientBuilder.create()
                    .disableCookieManagement().useSystemProperties();
            this.httpClient = createClient(builder, httpClientConnectionManager,
                    httpClientProperties);
            return this.httpClient;
        }

        @Bean
        @ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true)
        public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
                                              HttpClientConnectionManager httpClientConnectionManager,
                                              FeignHttpClientProperties httpClientProperties) {
            this.httpClient = createClient(httpClientFactory.createBuilder(),
                    httpClientConnectionManager, httpClientProperties);
            return this.httpClient;
        }

        private CloseableHttpClient createClient(HttpClientBuilder builder,
                                                 HttpClientConnectionManager httpClientConnectionManager,
                                                 FeignHttpClientProperties httpClientProperties) {
            RequestConfig defaultRequestConfig = RequestConfig.custom()
                    .setConnectTimeout(httpClientProperties.getConnectionTimeout())
                    .setRedirectsEnabled(httpClientProperties.isFollowRedirects())
                    .build();
            CloseableHttpClient httpClient = builder
                    .setDefaultRequestConfig(defaultRequestConfig)
                    .setConnectionManager(httpClientConnectionManager).build();
            return httpClient;
        }

        @PreDestroy
        public void destroy() throws Exception {
            this.connectionManagerTimer.cancel();
            if (this.httpClient != null) {
                this.httpClient.close();
            }
        }

    }

}
